iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
自我挑戰組

ASP.NET Core & Blazor系列 第 25

Day25 建立角色功能

  • 分享至 

  • xImage
  •  

首先建立裝載角色資料的 ViewModel,因為接下來的權限會以角色判斷,ASP.NET Core Identity 乘載角色的 Model 為 IdentityRole,裡面有太多不該讓使用者看到的資訊,通常會自己寫新的 ViewModel 以過濾多於資訊,這邊只呈現 RoleId、RoleName 跟 Role 底下所有使用者的名稱。

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace BlazorServer.ViewModels
{
    public class CustomRoleViewModel
    {
        public CustomRoleViewModel()
        {
            Users = new();
        }
        public string RoleId { get; set; }

        [Required(ErrorMessage = "角色名稱為必填")]
        public string RoleName { get; set; }
        public List<string> Users { get; set; }
    }
}

建立IRolesRepository.csRolesRepository.cs,這是專門處理角色的 Service,把基本的角色 CRUD(Create, Read, Update, Delete) 功能實作,再去Startup.cs註冊。

介面IRolesRepository.cs

using BlazorServer.ViewModels;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorServer.Services
{
    public interface IRolesRepository
    {
        Task<CustomRoleViewModel> GetRoleAsync(string RoleId);
        Task<List<CustomRoleViewModel>> GetRolesAsync();
        Task<ResultViewModel> CreateRoleAsync(CustomRoleViewModel model);
        Task<ResultViewModel> EditRoleAsync(CustomRoleViewModel model);
        Task<ResultViewModel> DeleteRoleAsync(string roleId);
        Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string RoleId);
        Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string RoleId);
    }
}

實作RolesRepository.cs,這邊注入的RoleManagerUserManager是 ASP.NET Core Identity 預設處理角色跟使用者的 Service,之前在Startup.cs寫的services.AddIdentity<IdentityUser, IdentityRole>()…就註冊了該功能,底下有各式 Role、User 相關API可以呼叫。
https://ithelp.ithome.com.tw/upload/images/20210925/20140893Gc7hOcTKyc.png

using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorServer.Services
{
    public class RolesRepository : IRolesRepository
    {
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<IdentityUser> _userManager;

        public RolesRepository(RoleManager<IdentityRole> roleManager,
            UserManager<IdentityUser> userManager)
        {
            _roleManager = roleManager;
            _userManager = userManager;
        }
        #region Roles
		// 取得單一角色
        public async Task<CustomRoleViewModel> GetRoleAsync(string RoleId)
        {
            var role = await _roleManager.FindByIdAsync(RoleId);
            var users = await _userManager.GetUsersInRoleAsync(role.Name);
            var result = new CustomRoleViewModel
            {
                RoleId = role.Id,
                RoleName = role.Name,
                Users = users.Select(u => u.UserName).ToList()
            };
            return result;
        }

		// 取得角色 List
        public async Task<List<CustomRoleViewModel>> GetRolesAsync()
        {
            var roles = _roleManager.Roles;
            var customRoles = new List<CustomRoleViewModel>();
            foreach (var role in roles)
            {
                customRoles.Add(new CustomRoleViewModel { RoleId = role.Id, RoleName= role.Name });
            }
            return await Task.Run(() => customRoles);
        }

		// 建立角色
        public async Task<ResultViewModel> CreateRoleAsync(CustomRoleViewModel model)
        {
            IdentityRole identityRole = new IdentityRole
            {
                Name = model.RoleName
            };
            var result = await _roleManager.CreateAsync(identityRole);
            if (result.Succeeded)
            {
                return new ResultViewModel
                {
                    Message = "角色建立成功!",
                    IsSuccess = true
                };
            }
            return new ResultViewModel
            {
                Message = "角色建立失敗!",
                IsSuccess = false
            };
        }

		// 編輯角色
        public async Task<ResultViewModel> EditRoleAsync(CustomRoleViewModel model)
        {
            var role = await _roleManager.FindByIdAsync(model. RoleId);

            if (role == null)
            {
                return new ResultViewModel
                {
                    Message = $"找不到 Id 為 {model.RoleId} 的角色",
                    IsSuccess = false
                };
            }
            role.Name = model.RoleName;
            var result = await _roleManager.UpdateAsync(role);
            if (result.Succeeded)
            {
                return new ResultViewModel
                {
                    Message = "角色更新成功!",
                    IsSuccess = true
                };
            }
            return new ResultViewModel
            {
                Message = "角色更新失敗!",
                IsSuccess = false
            };
        }

		// 刪除角色
        public async Task<ResultViewModel> DeleteRoleAsync(string roleId)
        {
            var role = await _roleManager.FindByIdAsync(roleId);

            if (role == null)
            {
                return new ResultViewModel
                {
                    Message = $"找不到 Id 為 {roleId} 的角色",
                    IsSuccess = false
                };
            }
            var result = await _roleManager.DeleteAsync(role);
            if (result.Succeeded)
            {
                return new ResultViewModel
                {
                    Message = "角色刪除成功!",
                    IsSuccess = true
                };
            }
            return new ResultViewModel
            {
                Message = "角色刪除失敗!",
                IsSuccess = false
            };
        }
        #endregion
    }
}

現在有處理資料的功能了,接下來要產生畫面。
RolesManagement.razor.cs

using BlazorServer.Services;
using BlazorServer.Shared;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;

namespace BlazorServer.Pages.RolesManagement
{
    public partial class RolesManagement
    {
        [Inject] protected IRolesRepository RolesRepository { get; set; }
        [Inject] protected IJSRuntime js { get; set; }
        private JsInteropClasses jsClass;
        public List<CustomRoleViewModel> Roles { get; set; } = new();
        protected override async Task OnInitializedAsync()
        {
            await loadData();
            jsClass = new(js);
        }
        private async Task loadData()
        {
            Roles = await RolesRepository.GetRolesAsync();
        }

        private async Task editRole(string roleId)
        {
            NavigationManager.NavigateTo($"RolesManagement/EditRole/{roleId}");
        }

        private async Task deleteRole(string roleId)
        {
            SweetConfirmViewModel sweetConfirm = new SweetConfirmViewModel()
            {
                RequestTitle = $"是否確定刪除角色{roleId}?",
                RequestText = "這個動作不可復原",
                ResponseTitle = "刪除成功",
                ResponseText = "角色被刪除了",
            };
            string jsonString = JsonSerializer.Serialize(sweetConfirm);
            bool result = await jsClass.Confirm(jsonString);
            if (result)
            {
                var deleted = await RolesRepository.DeleteRoleAsync(roleId);
                if (deleted.IsSuccess)
                {
                    await loadData();
                }
                else
                {
                    await jsClass.Alert(deleted.Message);
                }
            }
        }
    }
}

RolesManagement.razor

@page "/RolesManagement/RolesList"
@attribute [Authorize]

<h1>所有角色</h1>

@if (Roles.Any())
{
    <NavLink class="btn btn-primary mb-3" href="RolesManagement/CreateRole" Match="NavLinkMatch.All">
        新增角色
    </NavLink>

    foreach (var role in Roles)
    {
        <div class="card mb-3 w-25">
            <div class="card-header">
                Role Id : @role.RoleId
            </div>
            <div class="card-body">
                <h5 class="card-title">@role.RoleName</h5>
            </div>
            <div class="card-footer">
                <button type="button" class="btn btn-primary" @onclick="()=>editRole(role.RoleId)">
                    編輯角色
                </button>
                <button type="button" class="btn btn-danger" @onclick="()=>deleteRole(role.RoleId)">
                    刪除角色
                </button>
            </div>
        </div>
    }
}
else
{
    <div class="card w-25">
        <div class="card-header">
            還沒有角色
        </div>
        <div class="card-body">
            <h5 class="card-title">
                按底下的按鈕建立角色
            </h5>
            <NavLink class="btn btn-primary mb-3" href="RolesManagement/CreateRole" Match="NavLinkMatch.All">
                新增角色
            </NavLink>
        </div>
    </div>
}

然後去NavMenu.razor加入 NavLink 通往角色管理。

                <li class="nav-item px-3">
                    <NavLink class="nav-link" href="RolesManagement/RolesList" Match="NavLinkMatch.All">
                        <span class="bi bi-kanban-fill h4 p-2 mb-0" aria-hidden="true"></span> Roles
                    </NavLink>
                </li>

這時候開啟網站可以看到這樣的畫面,我們來加上新增角色的畫面並新增一個角色 Admin。
https://ithelp.ithome.com.tw/upload/images/20210925/20140893xezKUo4Pob.png
https://ithelp.ithome.com.tw/upload/images/20210925/20140893p1d6VhAB7R.png

CreateRole.razor.cs

using BlazorServer.Services;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;

namespace BlazorServer.Pages.RolesManagement
{
    public partial class CreateRole
    {
        [Inject] protected IRolesRepository RolesRepository { get; set; }
        [Inject] protected NavigationManager NavigationManager { get; set; }
        public CustomRoleViewModel Role { get; set; } = new();
        private async Task createRole()
        {
            await RolesRepository.CreateRoleAsync(Role);
            NavigationManager.NavigateTo("/RolesManagement/RolesList");
        }
    }
}

CreateRole.razor

@page "/RolesManagement/CreateRole"
@attribute [Authorize]

<EditForm class="mt-3" Model="Role" OnValidSubmit="createRole">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div class="form-group row">
        <label for="RoleName" class="col-sm-1 col-form-label">角色名稱</label>
        <div class="col-sm-3">
            <InputText @bind-Value="Role.RoleName" id="RoleName" class="form-control" placeholder="角色名稱"></InputText>
        </div>
    </div>

    <div class="form-group row">
        <div class="col-sm-10">
            <button type="submit" class="btn btn-primary">
                建立角色
            </button>
        </div>
    </div>
</EditForm>

有了建立功能就要有編輯功能,編輯完成或取消都直接跳轉回角色列表。
https://ithelp.ithome.com.tw/upload/images/20210925/20140893hQblxSloQo.png
EditRole.razor.cs

using BlazorServer.Services;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;

namespace BlazorServer.Pages.RolesManagement
{
    public partial class EditRole
    {
        [Inject] protected IRolesRepository RolesRepository { get; set; }
        [Inject] protected NavigationManager NavigationManager { get; set; }
        public CustomRoleViewModel Role { get; set; } = new();
        [Parameter]
        public string RoleId { get; set; }
        protected override async Task OnInitializedAsync()
        {
            var result = await RolesRepository.GetRoleAsync(RoleId);
            Role = new CustomRoleViewModel
            {
                RoleId = result.RoleId,
                RoleName = result.RoleName,
                Users = result.Users
            };
        }
        private async Task editRole()
        {
            await RolesRepository.EditRoleAsync(Role);
            NavigationManager.NavigateTo("/RolesManagement/RolesList");
        }
        public void Cancel()
        {
            NavigationManager.NavigateTo($"/RolesManagement/RolesList");
        }
    }
}

EditRole.razor

@page "/RolesManagement/EditRole/{RoleId}"
@attribute [Authorize]

<EditForm class="mt-3" Model="Role" OnValidSubmit="editRole">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div class="form-group row">
        <label for="RoleName" class="col-sm-1 col-form-label">角色名稱</label>
        <div class="col-sm-3">
            <InputText @bind-Value="Role.RoleName" id="RoleName" class="form-control" placeholder="角色名稱"></InputText>
        </div>
    </div>

    <div class="card mb-3 w-50">
        <div class="card-header">
            <h3>角色底下的使用者</h3>
        </div>
        <div class="card-body">
            @if (Role.Users.Any())
            {
                foreach (var user in Role.Users)
                {
                    <h5 class="card-title">@user</h5>
                }
            }
            else
            {
                <h5 class="card-title">目前該角色沒有指派給任何使用者</h5>
            }
        </div>
        <div class="card-footer">
            <button type="submit" class="btn btn-primary">更新角色</button>
            <button type="button" class="btn btn-danger" @onclick="Cancel">取消</button>
        </div>
    </div>
</EditForm>

角色 CRUD 功能大概就是這些,筆者只是用最簡單的方式處理,不過專案通常不會這麼簡單,還有其他細微功能要調整,明天來說明如何管理角色底下的使用者,以及如何套用角色授權。

Ref:Creating roles in asp net core

Ref:Get list of roles in asp net core

Ref:Edit role in asp net core


上一篇
Day24 <AuthorizeRouteView>運用
下一篇
Day26 指派角色給使用者
系列文
ASP.NET Core & Blazor30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言